#include "fixture.hh"
#include "testcase.hh"
#include "color.hh"
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <exception>
#include <typeinfo>

namespace k {
namespace test {

Fixture::Fixture(const std::string& name)
    : name_(name), run_(true), passed_(0), failed_(0) {
}

Fixture::~Fixture() {
    for (auto testcase : caseList_) {
        delete testcase;
    }
}

void Fixture::setup() {
    if (setup_) {
        setup_();
    }
}

void Fixture::tearDown() {
    if (tearDown_) {
        tearDown_();
    }
}

bool Fixture::onSetup(FixtureSetupMethod method) {
    setup_ = method;
    return true;
}

bool Fixture::onTearDown(FixtureTearDownMethod method) {
    tearDown_ = method;
    return true;
}

const std::string & Fixture::getName() const {
    return name_;
}

bool Fixture::addTestcase(Testcase * testcase) {
    caseList_.push_back(testcase);
    return true;
}

Testcase * Fixture::getTestcase(const std::string & name) {
    for (auto testcase : caseList_) {
        if (testcase->getName() == name) {
            return testcase;
        }
    }
    return nullptr;
}

void Fixture::disableTestcase(const std::string & name) {
    auto it = std::find_if(caseList_.begin(), caseList_.end(), [&](const Testcase* testcase) {
        return (testcase->getName() == name);
    });
    if (it == caseList_.end()) {
        return;
    }
    (*it)->disable();
}

void Fixture::enableTestcase(const std::string & name) {
    auto it = std::find_if(caseList_.begin(), caseList_.end(), [&](const Testcase* testcase) {
        return (testcase->getName() == name);
    });
    if (it == caseList_.end()) {
        return;
    }
    (*it)->enable();
}

void Fixture::runAllTestcase() {
    std::cout << "Run Suite [ " << green << name_ << black << " ]" << std::endl;
    std::size_t maxLength = 0;
    for (auto testcase : caseList_) {
        if (maxLength < testcase->getName().size()) {
            maxLength = testcase->getName().size();
        }
    }
    std::size_t pass = 0;
    std::size_t fail = 0;
    std::size_t disableCount = 0;
    std::size_t exception = 0;
    for (auto testcase : caseList_) {
        if (testcase->isEnable()) {
            std::cout << "Run [ " << green << std::left << std::setw(maxLength) << testcase->getName() << black << " ]";
            try {
                testcase->run();
                if (testcase->isSuccess()) {
                    pass++;
                    std::cout << " [" << green << std::left << std::setw(11) << " PASS " << black << "]" << std::endl;
                } else {
                    fail++;
                    std::cout << " ["<< red << std::left << std::setw(11) << " FAIL " << black << "]" << std::endl;
                    for (auto& error : testcase->getErrorList()) {
                        std::cout << red << "  > " << black << error << std::endl;
                    }
                }
            } catch (std::exception& e) {
                fail++;
                exception++;
                std::cout << " [ " << red << "EXCEPTION" << black << " ]" << std::endl;
                std::cout << red << "  > " << black << "[ " << green << typeid(e).name() << black << " ]" << std::endl;
                std::cout << "    > " << e.what() << std::endl;
            } catch (...) {
                fail++;
                exception++;
                std::cout << " [ " << red << "EXCEPTION" << black << " ]" << std::endl;
            }
            testcase->clearError();
        } else {
            disableCount++;
            std::cout << "Run [ " << std::left << std::setw(maxLength) << testcase->getName() << " ]";
            std::cout << " [" << std::left << std::setw(11) << " DISABLED " << "]" << std::endl;
        }
    }
    std::cout << "    [ " << std::left << std::setw(maxLength) << pass << " ] [ " << green << std::left << std::setw(10) << "PASS" << black << "] " << std::endl;
    if (fail) {
        std::cout << "    [ " << std::left << std::setw(maxLength) << fail << " ] [ " << red << std::left << std::setw(10) << "FAIL" << black << "] " << std::endl;
    }
    if (exception) {
        std::cout << "    [ " << std::left << std::setw(maxLength) << exception << " ] [ " << red << std::left << std::setw(10) << "EXCEPTION" << black << "] " << std::endl;
    }
    if (disableCount) {
        std::cout << "    [ " << std::left << std::setw(maxLength) << disableCount << " ] [ " << std::left << std::setw(10) << "DISABLE" << "] " << std::endl;
    }
    passed_ += pass;
    failed_ += fail;
}

bool Fixture::isEnable() {
    return run_;
}

void Fixture::disable() {
    run_ = false;
}

void Fixture::enable() {
    run_ = true;
}

std::size_t Fixture::getPassCases() {
    return passed_;
}

std::size_t Fixture::getFailedCases() {
    return failed_;
}

}
}
